/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败

// 创建类
class MyPromise {
  constructor(executor) {
    // 捕获错误
    try {
      // 执行器
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  // promise 状态
  status = PENDING;
  value = undefined; // 成功之后的值
  reason = undefined; // 失败之后的原因
  successCallback = []; // 成功回调, 数组的原因是可以存储多个
  failCallback = []; // 失败回调

  resolve = value => {
    // 如果状态不是等待，阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = value;
    // 判断成功回调是否存在，如存在则调用
    // 循环
    while (this.successCallback.length) {
      this.successCallback.shift()();
    }
    // es6写法
    // while (this.successCallback.length) this.successCallback.shift()(this.value)
  };
  reject = reason => {
    // 如果状态不是等待，阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败之后的原因
    this.reason = reason;
    // 判断失败回调是否存在，如存在则调用
    // 循环
    while (this.failCallback.length) this.failCallback.shift()();
  };
  then(successCallback, failCallback) {
    // 判断是否存在
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback
      ? failCallback
      : reason => {
          throw reason; // 错误信息传递下去
        };
    // 链试调用
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        // 异步调用
        setTimeout(() => {
          // 捕捉执行错误
          try {
            let x = successCallback(this.value);
            // 判断x的值是普通值还是promise对象
            // 如是普通值，则直接调用resolve
            // 如是promise对象，则查看promise对象返回的结果，根据返回的结果，决定调用resolve还是reject
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
        // resolve(x)
      } else if (this.status === REJECTED) {
        // failCallback(this.reason)
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 状态：等待
        // 将成功、失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise2;
    // 非链试调用
    // 判断状态
    // if (this.status === FULFILLED) {
    //   console.log(this.value)
    //   successCallback()
    // } else if (this.status === REJECTED) {
    //   failCallback(this.reason)
    // } else {
    //   // 状态：等待
    //   // 将成功、失败回调存储起来
    //   // this.successCallback = successCallback
    //   // this.failCallback = failCallback
    // this.successCallback.push(() => {
    //   setTimeout(() => {
    //     try {
    //       let x = successCallback(this.value);
    //       resolvePromise(promise2, x, resolve, reject);
    //     } catch (error) {
    //       reject(error);
    //     }
    //   }, 0);
    // });
    // this.failCallback.push(() => {
    //   setTimeout(() => {
    //     try {
    //       let x = failCallback(this.err);
    //       resolvePromise(promise2, x, resolve, reject);
    //     } catch (error) {
    //       reject(error);
    //     }
    //   }, 0);
    // });
    // }
  }
  // finally方法
  finally(callback) {
    return this.then(
      value => {
        callback();
        return value;
      },
      reason => {
        callback();
        throw reason;
      }
    );
  }
  // catch 方法
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }
  // all 方法（静态方法），接收的数组，返回一个promise对象，解决异步并发问题
  static all(array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        // 判断异步操作是否已经执行完毕
        if (index === array.length) {
          resolve(result);
        }
      }
      // 循环
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        // 判断
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(
            value => addData(i, value),
            reason => reject(reason)
          );
        } else {
          // 普通值
          addData(i, array[i]);
        }
      }
    });
  }
  // resolve 方法
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    // 自己返回自己
    return reject(new TypeError("不能自己返回自己"));
  }
  if (x instanceof MyPromise) {
    // 是promise对象
    // x.then(value => resolve(value), reason => reject(reason))
    x.then(resolve, reject);
  } else {
    // 是普通值
    resolve(x);
  }
}

// 测试
// 自己返回自己
let p1 = promise.then(value => {
  console.log(value);
  return p1;
});
p1.then(
  value => {
    console.log(value, "aaaaaaa");
  },
  reason => {
    console.log(reason, "ttttttttt");
  }
);

// then可以不传递参数，一次执行，直到有回调函数的时候
promise
  .then()
  .then()
  .then(
    value => console.log(value),
    reason => console.log(reason, "then可以不传递参数")
  );

function pv() {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve("pv");
    }, 2000);
  });
}

function pt() {
  return new MyPromise((resolve, reject) => {
    resolve("pt resolve");
    // reject('pt reject')
  });
}

MyPromise.all(["a", "b", pv(), pt(), "4"]).then(result => console.log(result, "all方法"));

MyPromise.resolve(100).then(value => console.log(value, "resolve方法-普通值"));
MyPromise.resolve(pv()).then(value => console.log(value, "resolve方法-Promise对象"));

pt()
  .finally(() => {
    console.log("finally");
  })
  .then(
    value => {
      console.log(value, "finally-susses");
    },
    reason => {
      console.log(reason, "finally-fail");
    }
  );
